﻿using System;

namespace l1cs.server.server.types
{
    public class Point
    {
        //private static Logger _log = Logger.getLogger(Point.class.getName());

        protected int _x = 0;
        protected int _y = 0;

        public Point()
        {
        }

        public Point( int x, int y )
        {
            _x = x;
            _y = y;
        }

        public Point( Point pt )
        {
            _x = pt._x;
            _y = pt._y;
        }

        /// <summary>
        /// 座標のX値を返す。
        /// </summary>
        /// <returns>座標のX値</returns>
        public int getX()
        {/*TTOK*/
            return _x;
        }

        /// <summary>
        /// 座標のX値を設定する。
        /// </summary>
        /// <param name="x">座標のX値</param>
        public void setX( int x )
        {/*TTOK*/
            _x = x;
        }

        /// <summary>
        /// 座標のY値を返す。
        /// </summary>
        /// <returns>座標のY値</returns>
        public int getY()
        {/*TTOK*/
            return _y;
        }

        /// <summary>
        /// 座標のY値を設定する。
        /// </summary>
        /// <param name="y">座標のY値</param>
        public void setY( int y )
        {/*TTOK*/
            _y = y;
        }

        public void set( Point pt )
        {
            _x = pt._x;
            _y = pt._y;
        }

        public void set( int x, int y )
        {
            _x = x;
            _y = y;
        }

        private static readonly int[] HEADING_TABLE_X = { 0, 1, 1, 1, 0, -1, -1, -1 };
        private static readonly int[] HEADING_TABLE_Y = { -1, -1, 0, 1, 1, 1, 0, -1 };

        /**
         * 指定された向きにこの座標をひとつ進める。
         * 
         * @param heading
         *            向き(0~7)
         */
        public void forward( int heading )
        {
            _x += HEADING_TABLE_X[heading];
            _y += HEADING_TABLE_Y[heading];
        }

        /**
         * 指定された向きと逆方向にこの座標をひとつ進める。
         * 
         * @param heading
         *            向き(0~7)
         */
        public void backward( int heading )
        {
            _x -= HEADING_TABLE_X[heading];
            _y -= HEADING_TABLE_Y[heading];
        }

        /**
         * 指定された座標への直線距離を返す。
         * 
         * @param pt
         *            座標を保持するPointオブジェクト
         * @return 座標までの直線距離
         */
        public double getLineDistance( Point pt )
        {
            long diffX = pt.getX() - this.getX();
            long diffY = pt.getY() - this.getY();
            return Math.Sqrt( ( diffX * diffX ) + ( diffY * diffY ) );
        }

        /**
         * 指定された座標までの直線タイル数を返す。
         * 
         * @param pt
         *            座標を保持するPointオブジェクト
         * @return 指定された座標までの直線タイル数。
         */
        public int getTileLineDistance( Point pt )
        {
            return Math.Max( Math.Abs( pt.getX() - getX() ), Math.Abs( pt.getY() - getY() ) );
        }

        /**
         * 指定された座標までのタイル数を返す。
         * 
         * @param pt
         *            座標を保持するPointオブジェクト
         * @return 指定された座標までのタイル数。
         */
        public int getTileDistance( Point pt )
        {
            return Math.Abs( pt.getX() - getX() ) + Math.Abs( pt.getY() - getY() );
        }

        /**
         * 指定された座標が画面内に見えるかを返す プレイヤーの座標を(0,0)とすれば見える範囲の座標は
         * 左上(2,-15)右上(15,-2)左下(-15,2)右下(-2,15)となる。 チャット欄に隠れて見えない部分も画面内に含まれる。
         * 
         * @param pt
         *            座標を保持するPointオブジェクト
         * @return 指定された座標が画面内に見える場合はtrue。そうでない場合はfalse。
         */
        public bool isInScreen( Point pt )
        {
            int dist = this.getTileDistance( pt );

            if ( dist > 17 )
            {
                return false;
            }
            else if ( dist <= 13 )
            {
                return true;
            }
            else
            {
                // 左右の画面外部分を除外
                // プレイヤーの座標を(15, 15)とした場合に(0, 0)にあたる座標からの距離で判断
                // Point pointZero = new Point(this.getX() - 15, this.getY() - 15);
                // int dist2 = pointZero.getTileDistance(pt);
                int dist2 = Math.Abs( pt.getX() - ( this.getX() - 15 ) )
                        + Math.Abs( pt.getY() - ( this.getY() - 15 ) );
                if ( 17 <= dist2 && dist2 <= 43 )
                {
                    return true;
                }
                return false;
            }
        }

        /**
         * 指定された座標と同じ座標かを返す。
         * 
         * @param pt
         *            座標を保持するPointオブジェクト
         * @return 指定された座標と同じ座標か。
         */
        public bool isSamePoint( Point pt )
        {
            return ( pt.getX() == getX() && pt.getY() == getY() );
        }

        virtual
        public int hashCode()
        {
            return 7 * getX() + getY();
        }

        virtual
        public bool equals( Object obj )
        {
            if ( !( obj is Point ) )
            {
                return false;
            }
            Point pt = (Point)obj;
            return ( this.getX() == pt.getX() ) && ( this.getY() == pt.getY() );
        }

        virtual
        public String toString()
        {
            return String.Format( "({0}, {1})", _x, _y );
        }
    }

}
